package by.dkusch.aircompany.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dkusch.aircompany.dao.UserProfileDao;
import by.dkusch.aircompany.model.filter.FilterUserProfile;
import by.dkusch.aircompany.model.filter.base.FilterEntity;
import by.dkusch.aircompany.model.tables.UserAccount;
import by.dkusch.aircompany.model.tables.UserAccount_;
import by.dkusch.aircompany.model.tables.UserGrant;
import by.dkusch.aircompany.model.tables.UserProfile;
import by.dkusch.aircompany.model.tables.UserProfile_;

@Repository
public class UserProfileDaoImpl extends AbstractDaoImpl<Long, UserProfile> implements UserProfileDao {

	public UserProfileDaoImpl() {
		super(UserProfile.class);
	}

	@Override
	public UserProfile getByIdWithLazyDetails(Long id) {

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		CriteriaQuery<UserProfile> root = cBuilder.createQuery(UserProfile.class);
		Root<UserProfile> criteria = root.from(UserProfile.class);

		root.select(criteria);
		root.where(cBuilder.equal(criteria.get(UserProfile_.id), id));
		root.select(criteria);
		criteria.fetch(UserProfile_.userGrant, JoinType.LEFT);
		criteria.fetch(UserProfile_.userAccount, JoinType.LEFT);
		root.distinct(true);

		TypedQuery<UserProfile> query = getEm().createQuery(root);
		UserProfile result = query.getSingleResult();
		return result;

	}

	@Override
	public Set<UserGrant> getGrants(Long userId) {
		if (userId != null) {
			UserProfile profile = getByIdWithLazyDetails(userId);
			if (profile != null) {
				return profile.getUserGrant();
			}
		}
		return new TreeSet<UserGrant>();

	}

	@Override
	public List<UserProfile> getAllItemsWithFilter(SingularAttribute<? super UserProfile, ?> attr, boolean ascending, int startRecord, int pageSize, FilterEntity<Long, ? super UserProfile> filter) {
		if (filter == null) {
			return getAllItems(attr, ascending, startRecord, pageSize);
		}

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		FilterUserProfile fc = (FilterUserProfile) filter;

		CriteriaQuery<UserProfile> criteria = cBuilder.createQuery(UserProfile.class);
		Root<UserProfile> fromUserProfile = criteria.from(UserProfile.class);

		@SuppressWarnings("unchecked")
		Join<UserProfile, UserAccount> fromAccount = (Join<UserProfile, UserAccount>) fromUserProfile.fetch(UserProfile_.userAccount, JoinType.INNER);

		OrderImpl order = new OrderImpl(fromUserProfile.get(UserProfile_.id), ascending);
		if (attr != null) {
			if (UserProfile_.userAccount.equals(attr)) {
				order = new OrderImpl(fromAccount.get(UserAccount_.email), ascending);
			} else {
				order = new OrderImpl(fromUserProfile.get(attr), ascending);
			}
		}

		List<Predicate> conditions = new ArrayList<Predicate>();

		if (fc.getCode() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromUserProfile.get(UserProfile_.code)), fc.getCode().toUpperCase()));
		}

		if (fc.getFirstName() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromUserProfile.get(UserProfile_.firstName)), fc.getFirstName().toUpperCase()));
		}

		if (fc.getLastName() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromUserProfile.get(UserProfile_.lastName)), fc.getLastName().toUpperCase()));
		}

		if (fc.getSecondName() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromUserProfile.get(UserProfile_.secondName)), fc.getSecondName().toUpperCase()));
		}

		if (fc.getEmail() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromAccount.get(UserAccount_.email)), fc.getEmail().toUpperCase()));
		}

		// fromUserProfile.fetch(UserProfile_.userAccount, JoinType.INNER);
		TypedQuery<UserProfile> query = getEm().createQuery(criteria.select(fromUserProfile).where(cBuilder.and(conditions.toArray(new Predicate[] {}))).orderBy(order));

		if (startRecord >= 0) {
			query.setFirstResult(startRecord);
			query.setMaxResults(pageSize);
		}

		List<UserProfile> results = query.getResultList();
		return results;

	}

}
